--[[
	- Garry's Mod Stranded -
	
	- Credits -
	
	- jA_c0p.
	- Wokkel.
	- prop_dynamic.
	- Muobman.
	- Chewgum.
	- RaBBish.
	- Mr. President.
	- Darkspider.
	
	And the model makers.
]]

GMS = {};
GMS.itemData = {};
GMS.DropQuery = {};

include("shared.lua");
include("cl_scoreboard.lua");
include("cl_panels.lua");
include("processes.lua");

include("unlocks.lua")
include("combinations.lua");

Tribes = {}
Skills = {}
Experience = {}
FeatureUnlocks = {}
GM.inventory = {};
GM.inventory_Max = 25;

local messages = {};
local processStart = 0;
local currentProcess = 0;
local processCompleteTime = 0;
local PlayerMeta = FindMetaTable("Player")

-- A function to tell the server that the player is ready.
hook.Add("Think", "playerReady", function()
	RunConsoleCommand("gms_plready");
	
	Hunger = 1000;
	Thirst = 1000;
	Sleepiness = 1000;
	
	GAMEMODE.NeedHud = vgui.Create("gms_NeedHud");
	GAMEMODE.SkillsHud = vgui.Create("gms_SkillsHud");
	
	hook.Remove("Think", "playerReady");
end);

-- Called when the default hud should be drawn.
function GM:HUDShouldDraw(name)
    if (name != "CHudHealth" and name != "CHudBattery") then
        return true;
    end;
end;

-- A function to draw things on the screen.
function GM:HUDPaint()
	self.BaseClass:HUDPaint();
	
	if (processCompleteTime > 1) then
		local width = ScrW() /3;
		local height = ScrH() /38;
		
		local maxWidth = ((RealTime() -processStart) / processCompleteTime) *width;
		
		if (maxWidth >= width) then 
			processCompleteTime = 0;
		end;
		
		surface.SetDrawColor(60, 60, 60, 210);
		surface.DrawRect(ScrW() *0.5 -(width *0.5), height, width, height);
		
		surface.SetDrawColor(20, 168, 220, 250);
		surface.DrawRect(ScrW() *0.5 -(width *0.5) +1, height +1, maxWidth -2, height -2);
		
		surface.SetDrawColor(0, 0, 0, 200);
		surface.DrawOutlinedRect(ScrW() *0.5 -(width *0.5), height, width, height);
		
		draw.SimpleText(currentProcess, "TargetIDSmall", ScrW() *0.5, height *1.5, Color(255, 255, 255, 255), 1, 1);
	end;

	if (#messages >= 1) then
		surface.SetFont("TargetID");
		
		for id, data in pairs(messages) do
			local text = data.Text
			local color = Color(data.Col.r, data.Col.g, data.Col.b, data.alpha);
			local line = ScrH() /2 +(data.drawline *20);
			data.width = surface.GetTextSize(text);
			data.width = data.width /2 +4;
			
			draw.SimpleTextOutlined(text, "TargetID", data.width, line, color, 1, 1, 1, Color(0, 0, 0, data.alpha));

			if (data.duration < CurTime()) then
				data.alpha = math.Clamp(data.alpha -60 *FrameTime(), 0, 255);
				
				if (data.alpha < 5) then
					table.remove(messages, id);
				end;
			end;
		end;
	end;
	
	surface.SetFont("TabLarge");
	
	for _, entity in ipairs(ents.GetAll()) do
		if (IsValid(entity) and entity:GetPos():Distance(LocalPlayer():GetPos()) <= 200) then
			local text = "";
			local class = entity:GetClass();
			local position = (entity:LocalToWorld(entity:OBBCenter())):ToScreen();
			
			if (class == "gms_resourcedrop") then
				text = (entity.Res or "Loading") .. ": (x" .. tostring(entity.Amount or "0") .. ")";
			end;
			
			if (table.HasValue(GMS.StructureEntities, class)) then
				text = (entity:GetNWString("Name") or "Loading");
				
				if (class == "gms_buildsite") then
					text = text .. entity:GetNetworkedString("Resources");
				end;
			end;
			
			if (text != "") then
				local w, h = surface.GetTextSize(text);

				draw.RoundedBox(4, position.x -(w *0.5) -4, position.y -(h *0.5), w +8, h +4, Color(20, 20, 20, 240));
				draw.DrawText(text, "TabLarge", position.x +1, position.y -(h *0.5) +2, Color(255, 255, 255, 255), 1);
			end;
		end;
	end;
end;

-- Called every frame.
function GM:Think()
	for id, info in pairs(GMS.DropQuery) do
		local entity = ents.GetByIndex(info.Index);
		
		if (IsValid(entity)) then
			entity.Res = info.Type;
            entity.Amount = info.Amount;
			
			table.remove(GMS.DropQuery, id);
        end;
    end;
end;

-- Hook the usermesssage to recieve notifies.
usermessage.Hook("gms_sendmessage", function(um)
	local text = um:ReadString();
	local duration = um:ReadShort();
	local color = um:ReadVector();
	
	for _, v in pairs(messages) do
		v.drawline = v.drawline +1;
	end;
	
	local message = {
		Text = text,
		Col = Color(color.x, color.y, color.z),
		drawline = 1,
		duration = CurTime() +duration,
		alpha = 255
	};
	
	table.insert(messages, message);
end);

--Make Progress bar
function GM.MakeProgressBar(um)
	currentProcess = um:ReadString()
	processStart = RealTime()
	processCompleteTime = um:ReadShort()
end

usermessage.Hook("gms_MakeProcessBar",GM.MakeProgressBar)

--Stop progress bar
usermessage.Hook("gms_StopProcessBar", function(um)
	processCompleteTime = 0;
end);

--Get skill
function GetSkill(skill)
	return Skills[skill] or 0
end

--Set skill
function GM.SetSkill(um)
	Skills[um:ReadString()] = um:ReadShort()

	GAMEMODE.inventory_Max = 25 +(GetSkill("Survival") *5)

	GAMEMODE.SkillsHud:RefreshSkills()
end

usermessage.Hook("gms_SetSkill",GM.SetSkill)

--Get XP
function GetXP(skill)
	return Experience[skill] or 0
end

--Set XP
function GM.SetXP(um)
	Experience[um:ReadString()] = um:ReadShort()
end

usermessage.Hook("gms_SetXP",GM.SetXP)

-- Hook a usermessage to receive a resource.
usermessage.Hook("gms_SetResource", function(um)
	local unique = um:ReadString();
	local amount = um:ReadShort();
	
	GAMEMODE.inventory[unique] = amount;
	
	if (GAMEMODE.inventory_Panel) then
		GAMEMODE.inventory_Panel:RefreshResources();
	end;
end);

-- Hook a usermessage to receive the max inventory space.
usermessage.Hook("gms_SetMaxResources", function(um)
	GAMEMODE.inventory_Max = um:ReadShort();
end);

-- A function to get a resource.
function GM.GetResource(unique)
	return GAMEMODE.inventory[unique] or 0;
end;

-- Hook a usermessage to toggle the skills menu.
usermessage.Hook("gms_ToggleSkillsMenu", function(um)
	GAMEMODE.SkillsHud:ToggleExtend();
end);

-- Hook a usermessage to open the combination menu,
usermessage.Hook("gms_OpenCombiMenu", function(um)
	local info = um:ReadString();
	
	if (!GAMEMODE.CombiMenu) then
		GAMEMODE.CombiMenu = vgui.Create("GMS_CombinationWindow");
	end;
	
	GAMEMODE.CombiMenu:SetTable(info);
	GAMEMODE.CombiMenu:SetVisible(true);
end);

-- Hook a usermessage to close the combination menu.
usermessage.Hook("gms_CloseCombiMenu", function(um)
	if (GAMEMODE.CombiMenu) then
		GAMEMODE.CombiMenu:SetVisible(false);
	end;
end);

-- A function to capitalize a string.
function string.Capitalize(text)
	local text = string.Explode("_", text);
	
	for k, v in pairs(text) do
		text[k] = string.upper(string.sub(v, 1, 1)) .. string.sub(v, 2);
	end;

	text = string.Implode("_", text);
	return text;
end;

/*---------------------------------------------------------
  Prop fading
---------------------------------------------------------*/
GM.FadingProps = {}
function GM.AddFadingProp(um)
	local mdl = um:ReadString()
	local pos = um:ReadVector()
	local dir = um:ReadVector()
	local speed = um:ReadShort()

	if !mdl or !pos or !dir or !speed then return end

	local ent = ents.Create("prop_physics")
	ent:SetModel(mdl)
	ent:SetPos(pos)
	ent:SetAngles(dir:Angle())
	ent:Spawn()

	ent.Alpha = 255
	ent.Speed = speed

	table.insert(GAMEMODE.FadingProps,ent)
end

usermessage.Hook("gms_CreateFadingProp",GM.AddFadingProp)

function GM.FadeFadingProps()
	local GM = GAMEMODE

	for k,v in pairs(GM.FadingProps) do
		if v.Alpha <= 0 then
			v.Entity:Remove()
			table.remove(GM.FadingProps,k)
		else
			v.Alpha = v.Alpha - (1 * v.Speed)
			v.Entity:SetColor(255,255,255,v.Alpha)
		end
	end
end

hook.Add("Think","gms_FadeFadingPropsHook",GM.FadeFadingProps)

/*---------------------------------------------------------
  Achievement Messages
---------------------------------------------------------*/
GM.AchievementMessages = {}

function GM.SendAchievement(um)
	local text = um:ReadString()

	local tbl = {}
	tbl.Text = text
	tbl.Alpha = 255

	table.insert(GAMEMODE.AchievementMessages,tbl)
end

usermessage.Hook("gms_sendachievement",GM.SendAchievement)


function GM.DrawAchievementMessages()
	for k,msg in pairs(GAMEMODE.AchievementMessages) do
		msg.Alpha = msg.Alpha - 1
		draw.SimpleTextOutlined(msg.Text,"ScoreboardHead",ScrW() / 2,ScrH() / 2,Color(255,255,255,msg.Alpha),1,1,0.5,Color(100,100,100,msg.Alpha))

		if msg.Alpha <= 0 then
			table.remove(GAMEMODE.AchievementMessages,k)
		end
	end
end

hook.Add("HUDPaint","gms_drawachievementmessages",GM.DrawAchievementMessages)
/*---------------------------------------------------------
  Needs
---------------------------------------------------------*/
function GM.SetNeeds(um)
	Sleepiness = um:ReadShort()
	Hunger = um:ReadShort()
	Thirst = um:ReadShort()
end
usermessage.Hook("gms_setneeds",GM.SetNeeds)

function GM.DecNeeds()

	if Sleepiness > 0 then Sleepiness = Sleepiness - 1 end
	if Thirst > 0 then Thirst = Thirst - 3 end
	if Hunger > 0 then Hunger = Hunger - 1 end
	timer.Simple(1,GAMEMODE.DecNeeds)

end
timer.Simple(1,GM.DecNeeds)


/*---------------------------------------------------------
  Sleep
---------------------------------------------------------*/
function GM.SleepOverlay()
	if !SleepFadeIn and !SleepFadeOut then return end

	if SleepFadeIn and SleepFade < 250 then
		SleepFade = SleepFade + 5
	elseif SleepFadeIn and SleepFade >= 255 then
		SleepFadeIn = false
	end

	if SleepFadeOut and SleepFade > 0 then
		SleepFade = SleepFade - 5
	elseif SleepFadeOut and SleepFade <= 0 then
		SleepFadeOut = false
	end

	surface.SetDrawColor(0,0,0,SleepFade)
	surface.DrawRect(0,0,ScrW(),ScrH())

	draw.SimpleText("Use the command \"!wakeup\" to wake up.","ScoreboardSub",ScrW() / 2, ScrH() / 2, Color(255,255,255,SleepFade),1,1)
end

hook.Add("HUDPaint","gms_sleepoverlay",GM.SleepOverlay)

function GM.StartSleep(um)
	SleepFadeIn = true
	SleepFade = 0
end

usermessage.Hook("gms_startsleep",GM.StartSleep)

function GM.StopSleep(um)
	SleepFadeOut = true
	SleepFade = 255
end

usermessage.Hook("gms_stopsleep",GM.StopSleep)

/*---------------------------------------------------------
  AFK
---------------------------------------------------------*/

function GM.AFKOverlay()
	if !AFKFadeIn and !AFKFadeOut then return end

	if AFKFadeIn and AFKFade < 250 then
		AFKFade = AFKFade + 5
	elseif AFKFadeIn and AFKFade >= 255 then
		AFKFadeIn = false
	end

	if AFKFadeOut and AFKFade > 0 then
		AFKFade = AFKFade - 5
	elseif AFKFadeOut and AFKFade <= 0 then
		AFKFadeOut = false
	end

	surface.SetDrawColor(0,0,0,AFKFade)
	surface.DrawRect(0,0,ScrW(),ScrH())

	draw.SimpleText("Use the command \"!afk\" to stop being afk.","ScoreboardSub",ScrW() / 2, ScrH() / 2, Color(255,255,255,AFKFade),1,1)
end

hook.Add("HUDPaint","gms_afkoverlay",GM.AFKOverlay)

function GM.StartAFK(um)
	AFKFadeIn = true
	AFKFade = 0
end

usermessage.Hook("gms_startafk",GM.StartAFK)

function GM.StopAFK(um)
	AFKFadeOut = true
	AFKFade = 255
end

usermessage.Hook("gms_stopafk",GM.StopAFK)

/*---------------------------------------------------------
  Unlock system
---------------------------------------------------------*/
function GM.AddUnlock(um)
	local text = um:ReadString()
	local GM = GAMEMODE

	if !GM.UnlockWindow then GM.UnlockWindow = vgui.Create("GMS_UnlockWindow") end

	GM.UnlockWindow:SetMouseInputEnabled(true)
	GM.UnlockWindow:SetUnlock(text)
	GM.UnlockWindow:SetVisible(true)
end

usermessage.Hook("gms_AddUnlock",GM.AddUnlock)

/*---------------------------------------------------------
  Drop resource menu
---------------------------------------------------------*/
function GM.OpenDropResourceWindow()
	local GM = GAMEMODE

	if !GM.ResourceWindow then GM.ResourceWindow = vgui.Create("GMS_ResourceDropWindow") end

	GM.ResourceWindow:RefreshList()
	GM.ResourceWindow:SetVisible(!GM.ResourceWindow:IsVisible())
end

concommand.Add("gms_OpenDropResourceWindow",GM.OpenDropResourceWindow)

/*---------------------------------------------------------
  Admin menu
---------------------------------------------------------*/
function GM.OpenAdminMenu()
	if !LocalPlayer():IsAdmin() then return end
	local GM = GAMEMODE

	if !GM.AdminMenu then
	GM.AdminMenu = vgui.Create("GMS_AdminMenu")
	GM.AdminMenu:SetVisible(false)
	end
	

	GM.AdminMenu:SetVisible(!GM.AdminMenu:IsVisible())
end

concommand.Add("gms_admin",GM.OpenAdminMenu)

/*---------------------------------------------------------
  Tribe menu
---------------------------------------------------------*/
function GM.OpenTribeMenu()
         local GM = GAMEMODE

         if !GM.TribeMenu then GM.TribeMenu = vgui.Create("GMS_TribeMenu") end

         GM.TribeMenu:SetVisible(!GM.TribeMenu:IsVisible())
end

concommand.Add("gms_tribemenu",GM.OpenTribeMenu)

/*---------------------------------------------------------
   Tribe system
---------------------------------------------------------*/
function GM.getTribes(data)
	team.SetUp(data:ReadShort(),data:ReadString(),Color(data:ReadShort(),data:ReadShort(),data:ReadShort(),255))
end
usermessage.Hook("recvTribes",GM.getTribes)

function GM.ReceiveTribe(data)
	local name = data:ReadString()
	local id = data:ReadShort()
	local red = data:ReadShort()
	local green = data:ReadShort()
	local blue = data:ReadShort()
	team.SetUp(id,name,Color(red,green,blue,255))
end
usermessage.Hook("newTribe",GM.ReceiveTribe)

-- A function to sort a table by keys.
function table.SortByKeyz(tbl, desc)
	local temp = {};
	local output = {};

	for key, _ in pairs(tbl) do
		table.insert(temp, key);
	end;
	
	if (desc) then
		table.sort(temp, function(a, b) return a > b end);
	else
		table.sort(temp, function(a, b) return a < b end);
	end;
	
	for key, value in pairs(temp) do
		output[key] = {};
		output[key][value] = tbl[value];
	end;

	return output;
end;

local quickDelay = 0;

-- Hook a usermessage to popup the quick menu.
usermessage.Hook("gms_quickmenu", function(um)
	if (quickDelay < CurTime()) then
		local quickMenu = DermaMenu();
			quickMenu:AddOption("Plant Melon", function() RunConsoleCommand("gms_plantmelon") end);
			quickMenu:AddOption("Plant Banana", function() RunConsoleCommand("gms_plantbanana") end);
			quickMenu:AddOption("Plant Orange", function() RunConsoleCommand("gms_plantorange") end);
			quickMenu:AddOption("Plant Tree", function() RunConsoleCommand("gms_planttree") end);
			quickMenu:AddOption("Plant Grain", function() RunConsoleCommand("gms_plantgrain") end);
			quickMenu:AddOption("Plant BerryBush", function() RunConsoleCommand("gms_plantbush") end);
		quickMenu:Open(ScrW() /2 -40, ScrH() /2);
		
		-- Prevent spam.
		quickDelay = CurTime() +0.2;
	end;
end);

-- A function to retrieve the information from the given unique.
function GM.GetItemInfo(unique)
	local info = GMS.itemData[unique];
	
	if (info) then
		return info;
	end;
	
	-- Item doesn't exist in the database, notify the player in console.
	-- Shouldn't happen though.
	Error("Tried to create item in inventory, does not exist in the database.\n");
end;

-- A function to add information to the item database.
local function AddItemInfo(unique, info)
	local defaults = {
		name = info.name or "",
		description = info.description or "",
		model = info.model or "models/error.mdl"
	};
	
	table.Merge(defaults, info);
	GMS.itemData[unique] = defaults;
end;

AddItemInfo("Orange_Seeds", {
	name 		= "Orange Seeds",
	description = "Orange seeds to plant.",
	model 		= "models/Weapons/w_bugbait.mdl"
});

AddItemInfo("Wood", {
	name 		= "Wood",
	description = "Some wood to build stuff.",
	model 		= "models/props_debris/wood_board06a.mdl"
});

AddItemInfo("Grain_Seeds", {
	name 		= "Grain Seeds",
	description	= "Grain seeds to plant. Grows grain.",
	model		= "models/Weapons/w_bugbait.mdl"
});

AddItemInfo("Bass", {
	name 		= "Bass Fish",
	description = "Bass fish, used in cooking.",
	model 		= "models/props_debris/wood_board06a.mdl"
});

AddItemInfo("Trout", {
	name 		= "Trout Fish",
	description = "Trout fish, used in cooking.",
	model 		= "models/props_debris/wood_board06a.mdl"
});

AddItemInfo("Spices", {
	name 		= "Spices",
	description = "Spices, used in cooking.",
	model 		= "models/props_debris/wood_board06a.mdl"
});

AddItemInfo("Water_Bottles", {
	name 		= "Water Bottle",
	description = "Water bottle, drink from it.",
	model 		= "models/props_junk/glassjug01.mdl",
	command 	= "gms_DrinkBottle"
});

AddItemInfo("Banana_Seeds", {
	name 		= "Banana Seeds",
	description = "Used to plant bananas.",
	model 		= "models/Weapons/w_bugbait.mdl"
});